package com.zb.excel.importExcel.listener;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.metadata.CellExtra;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.zb.constant.RedisImportDataConstant;
import com.zb.entity.*;
import com.zb.excel.UserExcelImport;
import com.zb.redis.RedisUtil;
import com.zb.secure.BCryptUtil;
import com.zb.service.ZjUserContactService;
import com.zb.service.ZjUserLevelService;
import com.zb.service.ZjUserService;
import com.zb.service.ZjUserTypeService;
import com.zb.utils.RandomUtil;
import com.zb.utils.TransactionUtil;
import com.zb.utils.VerificUtil;
import com.zb.utils.ZbExcelFieldUtil;
import com.zb.websocket.server.ProgressWebSocketServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;

import java.math.BigDecimal;
import java.util.*;

/**
 * @Author WEN
 * @Date 2024/1/5 0005
 **/
public class UserExcelImportListener implements ReadListener<UserExcelImport> {
    private static final Logger logger = LoggerFactory.getLogger(UserExcelImportListener.class);
    private static int BATCH_COUNT = 300; // 每批次执行插入数据库数量
    private boolean isFirstSend; // 是否首次发送进度
    private boolean isTwoSend; // 是否二次发送进度
    private boolean isEndSend; // 是否最后一次发送进度
    private int excelTotalRowCount; // Excel总数量（此包含空行的数量）
    private int totalCount; // 导入的总数量
    private int batch_no;  // 导入的批次
    private boolean isSendMessage;
    private List<ZjUser> addUserList;
    private List<ZjUser> updateUserList;
    private List<ZjUserContact> addContactList;
    private List<ZjUserContact> updateContactList;
    private List<ZjUserType> typeList;
    private List<ZjUserLevel> levelList;
    private StringBuffer message;

    private AccounLoginInfo loginInfo;
    private String userPrefix;

    // 校验数据重复的缓存key
    private String check_data_repeat_key = RedisImportDataConstant.EXCEL + "check-user-data-repeat:";
    private Map<String, Object> dataRepeatMap;
    // 当前执行的数量
    private long currentExcuteCount = 0;
    private List<UserExcelImport> importFailt;
    private List<UserExcelImport> importDataList;
    private String CACHE_IMPORT_DATA_REDIS_KEY = RedisImportDataConstant.USER_IMPORT;

    // 进度uuid识别码
    private String progressUuid;
    private PlatformTransactionManager transactionManager;
    private ZjUserService userService;
    private ZjUserContactService userContactService;
    private ZjUserTypeService userTypeService;
    private ZjUserLevelService userLevelService;

    public UserExcelImportListener(AccounLoginInfo loginInfo, String userPrefix, String progressUuid, PlatformTransactionManager transactionManager, ZjUserService userService,
                                   ZjUserContactService userContactService, ZjUserTypeService userTypeService, ZjUserLevelService userLevelService) {
        this.isFirstSend = true;
        this.isTwoSend = true;
        this.isEndSend = false;
        this.excelTotalRowCount = 0;
        this.totalCount = 0;
        this.initData();
        this.batch_no = 0;
        this.isSendMessage = false;
        this.message = new StringBuffer();
        this.progressUuid = progressUuid;
        this.check_data_repeat_key = check_data_repeat_key + loginInfo.getCustAccId() + ":" + this.progressUuid;
        this.loginInfo = loginInfo;
        this.userPrefix = userPrefix;
        this.importFailt = new ArrayList<>();
        this.CACHE_IMPORT_DATA_REDIS_KEY = this.CACHE_IMPORT_DATA_REDIS_KEY + loginInfo.getCustAccId() + this.progressUuid;

        this.transactionManager = transactionManager;
        this.userService = userService;
        this.userContactService = userContactService;
        this.userTypeService = userTypeService;
        this.userLevelService = userLevelService;
    }

    /**
     * 初始化数据
     */
    private void initData() {
        this.importDataList = new ArrayList<>();
        this.addUserList = new ArrayList<>(BATCH_COUNT);
        this.updateUserList = new ArrayList<>(BATCH_COUNT);
        this.addContactList = new ArrayList<>(BATCH_COUNT);
        this.updateContactList = new ArrayList<>(BATCH_COUNT);
        this.typeList = new ArrayList<>(BATCH_COUNT);
        this.levelList = new ArrayList<>(BATCH_COUNT);
    }

    @Override
    public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
        this.excelTotalRowCount = context.readSheetHolder().getApproximateTotalRowNumber();
        this.totalCount = this.excelTotalRowCount -2;
        if (this.isFirstSend) {
            this.message.append("数据正在解析中，请稍等...").append("</br>");
            ProgressWebSocketServer.send(this.progressUuid, false,  new ArrayList(), this.message.toString());
            this.isFirstSend = false;
        }
    }

    @Override
    public void invoke(UserExcelImport userExcelImport, AnalysisContext analysisContext) {
        if (this.isTwoSend) {
            this.message.append("数据解析成功，正在导入中..").append("</br>");
            ProgressWebSocketServer.send(this.progressUuid, false,  new ArrayList(), this.message.toString());
            this.isTwoSend = false;
        }
        this.currentExcuteCount++;
        analysisContext.readWorkbookHolder().setIgnoreEmptyRow(false);
        int rowNo = analysisContext.readRowHolder().getRowIndex() + 1; // Excel行号
        if (ZbExcelFieldUtil.isLineNullValue(userExcelImport)) {
            this.totalCount--;
            return;
        }
        if (checkEmpty(rowNo, userExcelImport)) {
            sendMessage();
            return;
        }
        if (checkDataRepeat(rowNo, userExcelImport)) {
            sendMessage();
            return;
        }
        Map<String, Object> mapKey = getCacheData();
        ZjUserType type = null;
        if (null != mapKey.get("type_" + userExcelImport.getUserType())) {
            type = JSON.parseObject(JSON.toJSONString(mapKey.get("type_" + userExcelImport.getUserType())), ZjUserType.class);
        } else {
            type = new ZjUserType();
            type.setId(IdWorker.getId());
            type.setCustAccId(loginInfo.getCustAccId());
            type.setName(userExcelImport.getUserType());
            type.setDisabled(false);
            typeList.add(type);
            mapKey.put("type_" + userExcelImport.getUserType(), type);
            RedisUtil.setCache(this.CACHE_IMPORT_DATA_REDIS_KEY, JSON.toJSONString(mapKey));
        }
        ZjUserLevel level = null;
        if (StringUtils.isNotBlank(userExcelImport.getLevel())) {
            if (null != mapKey.get("level_" + userExcelImport.getLevel())) {
                level = JSON.parseObject(JSON.toJSONString(mapKey.get("level_" + userExcelImport.getLevel())), ZjUserLevel.class);
            } else {
                level = new ZjUserLevel();
                level.setId(IdWorker.getId());
                level.setCustAccId(loginInfo.getCustAccId());
                level.setName(userExcelImport.getLevel());
                level.setDisabled(false);
                levelList.add(level);
                mapKey.put("level_" + userExcelImport.getLevel(), level);
                RedisUtil.setCache(this.CACHE_IMPORT_DATA_REDIS_KEY, JSON.toJSONString(mapKey));
            }
        }
        ZjUser user = null;
        if (null != mapKey.get("account_" + userExcelImport.getPhone())) {
            user = JSON.parseObject(JSON.toJSONString(mapKey.get("account_" + userExcelImport.getPhone())), ZjUser.class);
            if (StringUtils.isNotBlank(userExcelImport.getCode()) && null != mapKey.get("code_" + userExcelImport.getCode())) {
                if (!Objects.equals(user.getId(), (Long) mapKey.get("code_" + userExcelImport.getCode()))) {
                    this.importFailt.add(userExcelImport);
                    message.append("C").append(rowNo).append("单元 客户编码已存在系统中").append("</br>");
                    sendMessage();
                    return;
                }
            }
            user.setCode(userExcelImport.getCode());
            user.setUserTypeId(type.getId());
            user.setUserType(type.getName());
            user.setLevelId(null != level? level.getId() : 0L);
            user.setLevel(null != level? level.getName() : "");
            updateUserList.add(user);
        } else {
            if (StringUtils.isNotBlank(userExcelImport.getCode()) && null != mapKey.get("code_" + userExcelImport.getCode())) {
                this.importFailt.add(userExcelImport);
                message.append("C").append(rowNo).append("单元 客户编码已存在系统中").append("</br>");
                sendMessage();
                return;
            }
            user = new ZjUser();
            user.setId(IdWorker.getId());
            user.setCustAccId(loginInfo.getCustAccId());
            user.setPhone(userExcelImport.getPhone());
            user.setAccount(userExcelImport.getPhone());
            user.setSalt(BCryptUtil.generatorSalt());
            user.setPassword(BCryptUtil.encryPassword(user.getPhone().substring(5), user.getSalt()));
            user.setCode(StringUtils.isNotBlank(userExcelImport.getCode())? userExcelImport.getCode() : userPrefix + RandomUtil.random16());
            user.setName(userExcelImport.getUsername());
            user.setNickname(user.getName());
            user.setStatus(0);
            user.setAvatar("");
            user.setMoney(new BigDecimal("0.0"));
            user.setUserTypeId(type.getId());
            user.setUserType(type.getName());
            user.setLevelId(null != level? level.getId() : 0L);
            user.setLevel(null != level? level.getName() : "");
            user.setSelectKnowledgeAuthor(userExcelImport.getSelectKnowledgeAuthor());
            user.setCreateEmpId(loginInfo.getId());
            user.setUpdateEmpId(loginInfo.getId());
            addUserList.add(user);
            if (StringUtils.isBlank(userExcelImport.getCode())) {
                mapKey.put("code_" + user.getCode(), user.getId());
            }
            mapKey.put("account_" + user.getPhone(), user);
        }
        ZjUserContact contact = null;
        if (null != mapKey.get("contact_" + user.getId())) {
            contact = JSON.parseObject(JSON.toJSONString(mapKey.get("contact_" + user.getId())), ZjUserContact.class);
            contact.setPhone(userExcelImport.getPhone());
            contact.setUsername(userExcelImport.getUsername());
            contact.setEmail(userExcelImport.getEmail());
            contact.setProvince(userExcelImport.getProvince());
            contact.setCity(userExcelImport.getCity());
            contact.setArea(userExcelImport.getArea());
            contact.setTown("");
            contact.setAddress(userExcelImport.getAddress());
            updateContactList.add(contact);
        } else {
            contact = new ZjUserContact();
            contact.setId(IdWorker.getId());
            contact.setCustAccId(loginInfo.getCustAccId());
            contact.setUserId(user.getId());
            contact.setMainContact(true);
            contact.setUsername(userExcelImport.getUsername());
            contact.setPhone(userExcelImport.getPhone());
            contact.setEmail(StringUtils.isNotBlank(userExcelImport.getEmail())? userExcelImport.getEmail() : "");
            contact.setProvince(userExcelImport.getProvince());
            contact.setCity(userExcelImport.getCity());
            contact.setArea(userExcelImport.getArea());
            contact.setTown("");
            contact.setAddress(userExcelImport.getAddress());
            addContactList.add(contact);
            mapKey.put("contact_" + user.getId(), contact);
        }
        RedisUtil.setCache(this.CACHE_IMPORT_DATA_REDIS_KEY, JSON.toJSONString(mapKey));
        this.importDataList.add(userExcelImport);
        if (addUserList.size() >= BATCH_COUNT || updateUserList.size() >= BATCH_COUNT) {
            excuteSave();
        }
        sendMessage();
    }

    /**
     * 校验必填字段是否为空
     * @param rowNo
     * @param userExcelImport
     * @return
     */
    private boolean checkEmpty(int rowNo, UserExcelImport userExcelImport) {
        if (StringUtils.isBlank(userExcelImport.getUsername())) {
            this.importFailt.add(userExcelImport);
            message.append("A").append(rowNo).append("单元 客户姓名不能为空").append("</br>");
            return true;
        }
        if (userExcelImport.getUsername().length() > 100) {
            this.importFailt.add(userExcelImport);
            message.append("A").append(rowNo).append("单元 客户姓名限制100个字符").append("</br>");
            return true;
        }
        if (StringUtils.isBlank(userExcelImport.getPhone())) {
            this.importFailt.add(userExcelImport);
            message.append("B").append(rowNo).append("单元 客户手机号不能为空").append("</br>");
            return true;
        }
        if (!VerificUtil.phoneVerific(userExcelImport.getPhone())) {
            this.importFailt.add(userExcelImport);
            message.append("B").append(rowNo).append("单元 客户手机号格式错误").append("</br>");
            return true;
        }
        if (StringUtils.isNotBlank(userExcelImport.getCode()) && userExcelImport.getCode().length() > 60) {
            this.importFailt.add(userExcelImport);
            message.append("C").append(rowNo).append("单元 客户编码长度限制60个字符").append("</br>");
            return true;
        }
        if (StringUtils.isBlank(userExcelImport.getProvince())) {
            this.importFailt.add(userExcelImport);
            message.append("D").append(rowNo).append("单元 客户地址-省不能为空").append("</br>");
            return true;
        }
        if (userExcelImport.getProvince().length() > 60) {
            this.importFailt.add(userExcelImport);
            message.append("D").append(rowNo).append("单元 客户地址-省长度限制60个字符").append("</br>");
            return true;
        }
        if (StringUtils.isBlank(userExcelImport.getCity())) {
            this.importFailt.add(userExcelImport);
            message.append("E").append(rowNo).append("单元 客户地址-市不能为空").append("</br>");
            return true;
        }
        if (userExcelImport.getCity().length() > 60) {
            this.importFailt.add(userExcelImport);
            message.append("E").append(rowNo).append("单元 客户地址-市长度限制60个字符").append("</br>");
            return true;
        }
        if (StringUtils.isBlank(userExcelImport.getArea())) {
            this.importFailt.add(userExcelImport);
            message.append("F").append(rowNo).append("单元 客户地址-县/区不能为空").append("</br>");
            return true;
        }
        if (userExcelImport.getArea().length() > 60) {
            this.importFailt.add(userExcelImport);
            message.append("F").append(rowNo).append("单元 客户地址-县/区长度限制60个字符").append("</br>");
            return true;
        }
        if (StringUtils.isBlank(userExcelImport.getAddress())) {
            this.importFailt.add(userExcelImport);
            message.append("G").append(rowNo).append("单元 客户详细地址不能为空").append("</br>");
            return true;
        }
        if (userExcelImport.getAddress().length() > 200) {
            this.importFailt.add(userExcelImport);
            message.append("G").append(rowNo).append("单元 客户详细地址长度限制200个字符").append("</br>");
            return true;
        }
        if (StringUtils.isBlank(userExcelImport.getUserType())) {
            this.importFailt.add(userExcelImport);
            message.append("H").append(rowNo).append("单元 客户分类不能为空").append("</br>");
            return true;
        }
        if (userExcelImport.getUserType().length() > 20) {
            this.importFailt.add(userExcelImport);
            message.append("H").append(rowNo).append("单元 客户分类长度限制20个字符").append("</br>");
            return true;
        }
        if (null == userExcelImport.getSelectKnowledgeAuthor()) {
            this.importFailt.add(userExcelImport);
            message.append("I").append(rowNo).append("单元 知识库查看权限不能为空").append("</br>");
            return true;
        }
        if (StringUtils.isNotBlank(userExcelImport.getLevel()) && userExcelImport.getLevel().length() > 20) {
            this.importFailt.add(userExcelImport);
            message.append("J").append(rowNo).append("单元 客户级别长度限制20个字符").append("</br>");
            return true;
        }
        if (StringUtils.isNotBlank(userExcelImport.getEmail()) && userExcelImport.getEmail().length() > 200) {
            this.importFailt.add(userExcelImport);
            message.append("K").append(rowNo).append("单元 客户邮箱长度限制20个字符").append("</br>");
            return true;
        }
        return false;
    }

    /**
     * 校验数据是否重复
     * @param rowNo
     * @param userExcelImport
     * @return
     */
    private boolean checkDataRepeat(int rowNo, UserExcelImport userExcelImport) {
        boolean isDataRepeat = false;
        Object byKey = RedisUtil.getByKey(check_data_repeat_key);
        dataRepeatMap = JSON.parseObject((String) byKey, HashMap.class);
        if (null == dataRepeatMap) {
            dataRepeatMap = new HashMap<>();
        }
        if (null != dataRepeatMap.get("account_" + userExcelImport.getPhone())) {
            message.append("B").append(rowNo).append("单元 客户手机号已重复填写").append("</br>");
            isDataRepeat = true;
        } else {
            dataRepeatMap.put("account_" + userExcelImport.getPhone(), true);
        }
        if (StringUtils.isNotBlank(userExcelImport.getCode())) {
            if (null != dataRepeatMap.get("code_" + userExcelImport.getCode())) {
                message.append("C").append(rowNo).append("单元 客户编码已重复填写").append("</br>");
                isDataRepeat = true;
            } else {
                dataRepeatMap.put("code_" + userExcelImport.getCode(), true);
            }
        }
        RedisUtil.setCache(check_data_repeat_key, JSON.toJSONString(dataRepeatMap));
        if (isDataRepeat) {
            this.importFailt.add(userExcelImport);
        }
        return isDataRepeat;
    }

    /**
     * 获取缓存的数据
     * @return
     */
    private Map<String, Object> getCacheData() {
        Map<String, Object> cacheData = null;
        Object byKey = RedisUtil.getByKey(this.CACHE_IMPORT_DATA_REDIS_KEY);
        if (null == byKey) {
            return new HashMap<>();
        }
        cacheData = JSON.parseObject((String) byKey, Map.class);
        return cacheData;
    }

    @Override
    public void onException(Exception exception, AnalysisContext context) throws Exception {
        RedisUtil.clearCache(check_data_repeat_key);
    }

    // 所有数据解析完执行
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        RedisUtil.clearCache(check_data_repeat_key);
        if (CollectionUtil.isNotEmpty(addUserList) || CollectionUtil.isNotEmpty(updateUserList)) {
            excuteSave();
        }
        if (!this.isEndSend) {
            ProgressWebSocketServer.send(this.progressUuid, true, 100, this.totalCount, this.importFailt, this.message.toString());
            this.isEndSend = true;
        }
    }

    /**
     * 保存数据
     */
    private void excuteSave() {
        String color = "#67C23A";
        this.batch_no++;
        this.message.append("<div style=color:").append(color).append(">解析完成，正在处理保存 [第 ").append(this.batch_no).append("批] 数据，请耐心等待...</div>");
        sendMessage();
        this.message.append("<div style=color:").append(color).append(">第 ").append(this.batch_no);
        TransactionStatus status = this.transactionManager.getTransaction(TransactionUtil.transactionDefinition());
        try {
            if (CollectionUtil.isNotEmpty(this.addUserList)) userService.saveBatch(this.addUserList);
            if (CollectionUtil.isNotEmpty(this.updateUserList)) userService.updateBatchById(this.updateUserList);
            if (CollectionUtil.isNotEmpty(this.addContactList)) userContactService.saveBatch(this.addContactList);
            if (CollectionUtil.isNotEmpty(this.updateContactList)) userContactService.updateBatchById(this.updateContactList);
            if (CollectionUtil.isNotEmpty(this.typeList)) userTypeService.saveBatch(this.typeList);
            if (CollectionUtil.isNotEmpty(this.levelList)) userLevelService.saveBatch(this.levelList);
            this.transactionManager.commit(status);
            this.message.append(" 批导入成功</div>");
        }catch (Exception e) {
            e.printStackTrace();
            color = "#F56C6C";
            logger.error("客户档案第 {} 批导入失败，失败原因：{}", this.batch_no, e.getMessage());
            this.message.append(" 导入失败！原因：").append(e.getMessage()).append("</div>");
            this.transactionManager.rollback(status);
        }
        initData();
    }

    /**
     * 发送进度消息
     */
    private void sendMessage() {
        ProgressWebSocketServer.send(this.progressUuid, false, (1.0 * this.currentExcuteCount / this.excelTotalRowCount *100), this.importFailt, this.message.toString());
    }
}
